#include "regex/nfa.h"
#include "unit/assert.h"

using namespace regex;

void test_builder()
{
    Nfa nfa;

    ASSERT_TRUE(NfaBuilder::build("", nfa));
    ASSERT_TRUE(NfaBuilder::build("a", nfa));
    ASSERT_TRUE(NfaBuilder::build("abc", nfa));

    ASSERT_TRUE(NfaBuilder::build("^", nfa));
    ASSERT_TRUE(NfaBuilder::build("$", nfa));
    ASSERT_TRUE(NfaBuilder::build("^$", nfa));
    ASSERT_TRUE(NfaBuilder::build("^abc$", nfa));
    ASSERT_TRUE(NfaBuilder::build("^^$$", nfa));
    ASSERT_TRUE(NfaBuilder::build("$$^^", nfa));

    ASSERT_TRUE(NfaBuilder::build("a+", nfa));
    ASSERT_TRUE(NfaBuilder::build("a*", nfa));
    ASSERT_TRUE(NfaBuilder::build("a?", nfa));
    ASSERT_FALSE(NfaBuilder::build("+", nfa));
    ASSERT_FALSE(NfaBuilder::build("*", nfa));
    ASSERT_FALSE(NfaBuilder::build("?", nfa));

    ASSERT_FALSE(NfaBuilder::build("^+", nfa));
    ASSERT_FALSE(NfaBuilder::build("$+", nfa));
    ASSERT_FALSE(NfaBuilder::build("a++", nfa));
    ASSERT_FALSE(NfaBuilder::build("a**", nfa));
    ASSERT_FALSE(NfaBuilder::build("a??", nfa));
    
    ASSERT_TRUE(NfaBuilder::build("a+b*c?d", nfa));

    ASSERT_TRUE(NfaBuilder::build("|", nfa));
    ASSERT_TRUE(NfaBuilder::build("||", nfa));
    ASSERT_TRUE(NfaBuilder::build("a|b", nfa));
    ASSERT_TRUE(NfaBuilder::build("abc|abd", nfa));
    ASSERT_TRUE(NfaBuilder::build("^|a|$|", nfa));

    ASSERT_TRUE(NfaBuilder::build("()", nfa));
    ASSERT_TRUE(NfaBuilder::build("(|)", nfa));
    ASSERT_TRUE(NfaBuilder::build("a(b|d)", nfa));
    ASSERT_TRUE(NfaBuilder::build("a(b(c|d)|e|f(0|1)|g)", nfa));
    ASSERT_FALSE(NfaBuilder::build("a(b(c()d)e", nfa));
    ASSERT_FALSE(NfaBuilder::build("a(b(c()d)e))", nfa));

    ASSERT_FALSE(NfaBuilder::build("[]", nfa));
    ASSERT_FALSE(NfaBuilder::build("[b-a]", nfa));
    ASSERT_TRUE(NfaBuilder::build("[a-zA-Z]", nfa));

    ASSERT_TRUE(NfaBuilder::build("[a-zA-Z0-9.-_]+@[a-zA-Z0-9-]+.[a-zA-Z]+(.[a-zA-Z]+)?", nfa));
}

void test_matcher()
{
    Nfa nfa;
    MatchResult mr;

    NfaBuilder::build("", nfa);
    ASSERT_TRUE(NfaMatcher::match(nfa, "", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "a", mr));

    NfaBuilder::build("a", nfa);
    ASSERT_FALSE(NfaMatcher::match(nfa, "", mr));
    ASSERT_FALSE(NfaMatcher::match(nfa, "b", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "a", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "ab", mr));

    NfaBuilder::build("^", nfa);
    ASSERT_TRUE(NfaMatcher::match(nfa, "", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "a", mr));

    NfaBuilder::build("^a", nfa);
    ASSERT_FALSE(NfaMatcher::match(nfa, "ba", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "a", mr));

    NfaBuilder::build("$", nfa);
    ASSERT_FALSE(NfaMatcher::match(nfa, "a", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "", mr));

    NfaBuilder::build("a$", nfa);
    ASSERT_FALSE(NfaMatcher::match(nfa, "ab", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "a", mr));

    NfaBuilder::build("abc", nfa);
    ASSERT_FALSE(NfaMatcher::match(nfa, "ab", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "abc", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "abcd", mr));

    NfaBuilder::build("a+b", nfa);
    ASSERT_FALSE(NfaMatcher::match(nfa, "b", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "ab", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "aaab", mr));

    NfaBuilder::build("a*b", nfa);
    ASSERT_TRUE(NfaMatcher::match(nfa, "b", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "ab", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "aaab", mr));

    NfaBuilder::build("a?b", nfa);
    ASSERT_TRUE(NfaMatcher::match(nfa, "b", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "ab", mr));
    ASSERT_FALSE(NfaMatcher::match(nfa, "aaab", mr));

    NfaBuilder::build("a|b", nfa);
    ASSERT_TRUE(NfaMatcher::match(nfa, "b", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "a", mr));

    NfaBuilder::build("abc|def", nfa);
    ASSERT_TRUE(NfaMatcher::match(nfa, "abc", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "def", mr));

    NfaBuilder::build("a(bc)d", nfa);
    ASSERT_TRUE(NfaMatcher::match(nfa, "abcd", mr));

    NfaBuilder::build("a(bc|def)g", nfa);
    ASSERT_TRUE(NfaMatcher::match(nfa, "abcg", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "adefg", mr));

    NfaBuilder::build("[a-zA-Z]", nfa);
    ASSERT_FALSE(NfaMatcher::match(nfa, "019", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "abcg", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "AC", mr));

    NfaBuilder::build("[a-zA-Z0-9.-_]+@[a-zA-Z0-9-]+.[a-zA-Z]+(.[a-zA-Z]+)?$", nfa);
    ASSERT_FALSE(NfaMatcher::match(nfa, "liliang@g.mail.com.cn", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "liangli.lil@gmail.com", mr));
    ASSERT_TRUE(NfaMatcher::match(nfa, "liliang@gmail.com.cn", mr));
}

int main()
{
    test_builder();
    test_matcher();
    return 0;
}
